package com.ys.fmms.client;

import org.apache.commons.io.FileUtils;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.HttpClients;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.io.*;
import java.net.URLDecoder;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * page 第几个切片
 * pages 总切片数 = 文件大小 / 每个切片的大小
 * start 切片开始的位置
 * end 切片结束的位置
 *
 * Range bytes 0-255/256
 *
 * bytes=0-123 或 bytes=0- 从请求头为Range获取的当前文件的字节数范围
 */
@RestController
public class DownloadClient {
    private final static long PER_PAGE = 1024l * 1024l * 50l; // 50MB
    private final static String DOWN_PATH = "D:\\uploadVideo";
    ExecutorService pool = Executors.newFixedThreadPool(10);

    //文件大小 分片数量 文件名字
    // 探测 获取文件信息
    // 多线程分片下载
    // 最后一个分片下载完 开始合并
    @RequestMapping("/downloadFile")
    public String downloadFile() throws Exception {
        FileInfo fileInfo = download(0, 10, -1, null);
        if (fileInfo != null) {
            // 总共分片数 3
            long pages = fileInfo.fSize / PER_PAGE;
            for (long i = 0; i <= pages; i++) {
                // 此处需要注意减一，是由于下次一已经读取了一个字节不需要再读取，所以需要减1
                pool.submit(new DownLoad(i * PER_PAGE, (i + 1) * PER_PAGE-1 , i, fileInfo.fName));
            }
        }
        return "success";
    }

    class DownLoad implements Runnable {
        long start;
        long end;
        long page;
        String fName;

        public DownLoad(long start, long end, long page, String fName) {
            this.start = start;
            this.end = end;
            this.page = page;
            this.fName = fName;
        }

        @Override
        public void run() {
            try {
                download(start, end, page, fName);
            } catch (IOException e) {
                e.printStackTrace();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    class FileInfo {
        long fSize;
        String fName;

        public FileInfo(long fSize, String fName) {
            this.fName = fName;
            this.fSize = fSize;
        }
    }

    // 结束位置 - 开始位置 = 分片大小 page 当前是第几个分片 fName当前分片名
    private FileInfo download(long start, long end, long page, String fName) throws Exception {
        // 对每个切片文件命名 page+"-"+fName
        File file = new File(DOWN_PATH, page + "-" + fName);
        // 断点下载
        if (file.exists() && page != -1 && file.length() == PER_PAGE) {
            return null;
        }
        HttpClient client = HttpClients.createDefault();
        HttpGet httpGet = new HttpGet("http://127.0.0.1:8080/download");
        httpGet.setHeader("Range", "bytes=" + start + "-" + end);
        // 指定到这里就获取了要下载的文件的相关信息
        HttpResponse response = client.execute(httpGet);
        String fSize = response.getFirstHeader("fSize").getValue();
        // 下载服务端已经编码 所以客户端需要解码
        fName = URLDecoder.decode(response.getFirstHeader("fName").getValue(), "utf-8");
        HttpEntity entity = response.getEntity();
        InputStream is = entity.getContent();
        FileOutputStream fos = new FileOutputStream(file);
        byte[] buffer = new byte[1024];
        int ch;
        while ((ch = is.read(buffer)) != -1) {
            fos.write(buffer, 0, ch);
        }
        is.close();
        fos.flush();
        fos.close();
        if (end - Long.parseLong(fSize) > 0) {
            // 到这里已经是最后一个分片，需要进行合并的流程
            mergeFile(fName, page);
        }
        return new FileInfo(Long.parseLong(fSize), fName);
    }

    private void mergeFile(String fName, long page) throws Exception {
        File file = new File(DOWN_PATH, fName);
        BufferedOutputStream os = new BufferedOutputStream(new FileOutputStream(file));
        for (int i = 0; i <= page; i++) {
            // 每一个切片命名：page+"-"+fName
            File temFile = new File(DOWN_PATH, i + "-" + fName);
            // 这里判断切片文件不存在或除了最后一个切片其他切片并没有下载完成的情况
            while (!file.exists() || (i != page && temFile.length() < PER_PAGE)) {
                Thread.sleep(100);
            }
            byte[] bytes = FileUtils.readFileToByteArray(temFile);
            os.write(bytes);
            os.flush();
            temFile.delete();
        }
        File file1 = new File(DOWN_PATH, -1 + "-null");
        file1.delete();
        os.flush();
        os.close();
    }
}
